/*
 * I write in my free time, for personal using
 * when i fell proud of it, i will give it to public
 * now,have nothing more to say
 */
package com.quangtin3.utils;

/**
 * abstract class for all self runnable class
 * @author quanghx
 */
public abstract class SelfRunnable implements ControlAble {

    private Thread internalThread;
    private boolean threadStarted;
    private volatile boolean stopRequested;
    private BooleanLock suspendRequested;
    private BooleanLock internalSuspended;

    /**
     * contructer, using default name
     * @param bstart: will start after create if true
     */
    public SelfRunnable(boolean bstart) {

        //initial states
        threadStarted = bstart;
        stopRequested = false;        
        suspendRequested = new BooleanLock(false);
        internalSuspended = new BooleanLock(false);

        //internal thread
        internalThread = new Thread(new Runnable() {

            public void run() {
                performJob();                
            }
        });

        //start if needed
        if (bstart) {
            internalThread.start();
        }
    }

    /**
     * constructer, start process after create and use default name
     */
    public SelfRunnable() {
        this(true);
    }

    /**
     * constructer, start process after create
     * @param name process name (internal thread name)
     */
    public SelfRunnable(String name) {
        this(true);
        setThreadName(name);
    }

    /**
     * constructer
     * @param name name of this process (internal thread name)
     * @param bstart if true start after create, otherwise net call requestStart
     */
    public SelfRunnable(String name, boolean bstart) {
        this(bstart);
        setThreadName(name);
    }

    public void setThreadName(String name) {
        internalThread.setName(name);
    }

    public String getThreadName() {
        return internalThread.getName();
    }

    protected void performJob() {
        TunllLogger.sys.debug("start performJob");
        while (!stopRequested) {
            TunllLogger.sys.debug("process lo0op");
            try {
                waitWhileSuspended();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                continue;
            }

            try {
                elmProcess();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        TunllLogger.sys.debug("leave performJob");
    }

    abstract protected void elmProcess() throws InterruptedException;

    private void waitWhileSuspended() throws InterruptedException {
        synchronized (suspendRequested) {
            try {
                internalSuspended.setValue(true);
                suspendRequested.waitUntilFalse(0);
            } finally {
                internalSuspended.setValue(false);
            }
        }
    }

    /**
     * start process, need call only one.
     * if internal thread is stopped, what happend?
     */
    public synchronized void requestStart() {
        TunllLogger.sys.debug("requestStart");
        if (!threadStarted) {
            threadStarted = true;
            internalThread.start();
        }
    }

    /**
     * request stop this object
     */
    public void requestStop() {
        TunllLogger.sys.debug("requestStop");
        stopRequested = true;
        internalThread.interrupt();
    }

    /**
     * request suspend process
     */
    public void requestSuspend() {
        TunllLogger.sys.debug("requestSuspend");
        suspendRequested.setValue(true);
    }

    /**
     * request resume process
     */
    public void requestResume() {
        TunllLogger.sys.debug("requestResume");
        suspendRequested.setValue(false);
    }

    /**
     * is internal thread alive
     * @return
     */
    public boolean isAlive() {
        return internalThread.isAlive();
    }
}
